package com.lxd.sunso.product.service.impl;

import com.lxd.sunso.common.utils.PageUtils;
import com.lxd.sunso.common.utils.Query;
import com.lxd.sunso.product.entity.CustomerEntity;
import com.lxd.sunso.product.entity.DatatemplateEntity;
import com.lxd.sunso.product.entity.MachineEntity;
import com.lxd.sunso.product.service.CustomerService;
import com.lxd.sunso.product.service.DatatemplateService;
import com.lxd.sunso.product.service.MachineService;
import com.lxd.sunso.product.vo.CurveVo;
import com.lxd.sunso.product.vo.HistoryDataVo;
import com.lxd.sunso.product.vo.RealDataVo;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lxd.sunso.product.dao.HistroydataDao;
import com.lxd.sunso.product.entity.HistroydataEntity;
import com.lxd.sunso.product.service.HistroydataService;

import javax.servlet.http.HttpServletResponse;


@Service("histroydataService")
public class HistroydataServiceImpl extends ServiceImpl<HistroydataDao, HistroydataEntity> implements HistroydataService {

    @Autowired
    private MachineService machineService;

    @Autowired
    private DatatemplateService datatemplateService;

    @Autowired
    private CustomerService customerService;

    @Autowired
    private HistroydataDao histroydataDao;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<HistroydataEntity> page = this.page(
                new Query<HistroydataEntity>().getPage(params),
                new QueryWrapper<HistroydataEntity>()
        );
        return new PageUtils(page);
    }

    //导出记录
    @Override
    public void exportExcel(HttpServletResponse response, Map<String, Object> params) throws Exception {
        String machineName = (String) params.get("machineName");
        String datatempalteId = (String) params.get("datatemplateId");
        String startTime = (String) params.get("startTime");
        String endTime = (String) params.get("endTime");
        QueryWrapper<HistroydataEntity> queryWrapper = new QueryWrapper<>();
        MachineEntity machineEntity = null;

        if (!StringUtils.isEmpty(machineName)) {
            machineEntity = machineService.getOne(new QueryWrapper<MachineEntity>().eq("MachineName", machineName));
            if (machineEntity != null) {
                queryWrapper.eq("MachineID", machineEntity.getId());
            }
        }

        if (!StringUtils.isEmpty(datatempalteId)) {
            queryWrapper.eq("DatatemplateId", datatempalteId);
        }

        if (!StringUtils.isEmpty(startTime)) {
            queryWrapper.ge("CreateTime", startTime);
        }

        if (!StringUtils.isEmpty(endTime)) {
            endTime += " 23:59:59";
            queryWrapper.le("CreateTime", endTime);
        }

        queryWrapper.orderByDesc("CreateTime");

        List<DatatemplateEntity> datatemList = datatemplateService.list(new QueryWrapper<DatatemplateEntity>().eq("TemplateId", machineEntity.getTemplateid()));


        List<HistroydataEntity> entities = null;
        /*     //对结果集进行分段的操作
        int count = this.count(queryWrapper);
        //如果历史数据的结果集大于1000，则转换为线程池操作
        if (count > 1000) {
            int sums = count / datatemList.size(), total = 5;//总共的行数
            //对历史数据的结果集进行分段
            int[] size = new int[total];
            for (int i = 0; i < total; i++) {
                size[i] = sums / (total - i);
            }

            //创建线程池进行操作
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            List<HistroydataEntity> collect = new ArrayList<>();//返回结果
            List<Callable<List<HistroydataEntity>>> tasks = new ArrayList<>();
            for (int i = 0; i < size.length; i++) {
                tasks.add(new Callable<List<HistroydataEntity>>() {
                    @Override
                    public List<HistroydataEntity> call() throws Exception {

                        return null;
                    }
                });
            }

        } else {*/
        IPage<HistroydataEntity> page = this.page(new Query<HistroydataEntity>().getPage(params),
                queryWrapper);
        entities = page.getRecords();


        //历史记录
        //List<HistroydataEntity> entities = page.getRecords();
        MachineEntity finalMachineEntity = machineEntity;
        CustomerEntity customerEntity = customerService.getById(finalMachineEntity.getCustomerid());


        //封装detatemList为map,方便最后结果的封装
        Map<Long, DatatemplateEntity> datatemMap = new HashMap<>();
        for (DatatemplateEntity entity : datatemList) {
            datatemMap.put(entity.getId(), entity);
        }


        List<HistoryDataVo> list = entities.stream().map(entity -> {
            HistoryDataVo historyDataVo = new HistoryDataVo();
            DatatemplateEntity datatemplateEntity = datatemMap.get(entity.getDatatemplateid());
            //historyDataVo.setControllerName(datatemplateEntity.getControlname());
            //historyDataVo.setRegisterAddress(datatemplateEntity.getRegisteraddress());
            if (datatemplateEntity.getUnitname().contains("开/关")) {
                if (entity.getDatavalue().equals("1")) {
                    historyDataVo.setDataValue("开");
                } else {
                    historyDataVo.setDataValue("关");
                }
            } else {
                historyDataVo.setDataValue(entity.getDatavalue() + datatemplateEntity.getUnitname());
            }
            historyDataVo.setTime(entity.getCreatetime());
            return historyDataVo;
        }).collect(Collectors.toList());

        FileOutputStream fos = null;
        SXSSFWorkbook sxssfWorkbook = null;
        String fileName = "historyData";
        try {
            // 获取SXSSFWorkbook实例
            sxssfWorkbook = new SXSSFWorkbook();
            Sheet sheet = sxssfWorkbook.createSheet("Sheet");
            // 冻结最左边的两列、冻结最上面的一行
            // 即：滚动横向滚动条时，左边的第一、二列固定不动;滚动纵向滚动条时，上面的第一行固定不动。
            sheet.createFreezePane(2, 1);

            // 设置并获取到需要的样式
            XSSFCellStyle xssfCellStyleHeader = getAndSetXSSFCellStyleHeader(sxssfWorkbook);
            XSSFCellStyle xssfCellStyleOne = getAndSetXSSFCellStyleOne(sxssfWorkbook);
            XSSFCellStyle xssfCellStyleTwo = getAndSetXSSFCellStyleTwo(sxssfWorkbook);
            // 创建第一行,作为header表头
            Row header = sheet.createRow(0);

            // 循环创建header单元格(根据实际情况灵活创建即可)
            int datatemsize = datatemList.size();
            //反转集合
            Collections.reverse(datatemList);
            Row titleRow = (Row) sheet.createRow(0);

            Cell cell1 = titleRow.createCell(0);
            cell1.setCellType(Cell.CELL_TYPE_STRING);
            cell1.setCellValue("设备名称");
            for (int i = 1; i <= datatemsize; i++) {
                Cell cell = titleRow.createCell(i);
                cell.setCellType(Cell.CELL_TYPE_STRING);
                cell.setCellValue(datatemList.get(i - 1).getControlname());
            }
            Cell cell2 = titleRow.createCell(datatemsize + 1);
            cell2.setCellType(Cell.CELL_TYPE_STRING);
            cell2.setCellValue("时间");

            int j = 0;
            // 遍历创建行,导出数据
            for (int rownum = 1; rownum <= list.size() / datatemsize; rownum++) {
                Row row = sheet.createRow(rownum);
                // 循环创建单元格
                for (int cellnum = 0; cellnum < datatemsize + 2; cellnum++) {
                    Cell cell = row.createCell(cellnum);
                    // 根据行数,设置该行内的单元格样式
                    if (rownum % 2 == 1) { // 奇数
                        cell.setCellStyle(xssfCellStyleOne);
                    } else { // 偶数
                        cell.setCellStyle(xssfCellStyleTwo);
                    }
                    if (cellnum == 0) {
                        cell.setCellType(Cell.CELL_TYPE_STRING);
                        cell.setCellValue(machineName);
                    } else if (cellnum == datatemsize + 1) {
                        cell.setCellType(Cell.CELL_TYPE_STRING);
                        cell.setCellValue((list.get(j - 1).getTime()));
                    } else {
                        cell.setCellType(Cell.CELL_TYPE_STRING);
                        cell.setCellValue((list.get(j++).getDataValue()));
                    }
                }
            }
            // 在后面设置sheet
            setSheet(sheet);
            response.setHeader("Content-disposition", "attachment; filename=" + new String(fileName.getBytes("GB2312"), "8859_1") + ".xlsx");
            OutputStream os = response.getOutputStream();
            sxssfWorkbook.write(os);
            os.flush();
            os.close();
        } catch (Exception e) {

        } finally {
            try {
                if (sxssfWorkbook != null) {
                    // dispose of temporary files backing this workbook on disk -> 处
                    //     理SXSSFWorkbook导出excel时，产生的临时文件
                    sxssfWorkbook.dispose();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public RealDataVo listHistory(Map<String, Object> params) {
        String machineName = (String) params.get("machineName");
        String datatempalteId = (String) params.get("datatemplateId");
        String startTime = (String) params.get("startTime");
        String endTime = (String) params.get("endTime");
        String pagesize = (String) params.get("limit"); //每页的数量
        String currentpage = (String) params.get("page");//当前页

        MachineEntity machineEntity = machineService.getOne(new QueryWrapper<MachineEntity>().eq("machineName", machineName));
        List<DatatemplateEntity> datatemList = datatemplateService.list(new QueryWrapper<DatatemplateEntity>().eq("TemplateId", machineEntity.getTemplateid()));

        //封装detatemList为map,方便最后结果的封装
        Map<Long, DatatemplateEntity> datatemMap = new HashMap<>();
        for (DatatemplateEntity entity : datatemList) {
            datatemMap.put(entity.getId(), entity);
        }
        //获取设备所属的客户
        CustomerEntity customerEntity = customerService.getById(machineEntity.getCustomerid());
        //数据模板的数量
        int size = datatemList.size();
        //更新要查询的页数
        Integer limit = size * Integer.valueOf((String) params.get("limit"));
        params.put("limit", limit.toString());

        QueryWrapper<HistroydataEntity> queryWrapper = new QueryWrapper<>();

        if (!StringUtils.isEmpty(machineName)) {
            if (machineEntity != null) {
                queryWrapper.eq("MachineID", machineEntity.getId());
            }
        }

        if (!StringUtils.isEmpty(datatempalteId)) {
            queryWrapper.eq("DatatemplateId", datatempalteId);
        }

        if (!StringUtils.isEmpty(startTime)) {
            queryWrapper.ge("CreateTime", startTime);
        }


        if (!StringUtils.isEmpty(endTime)) {
            endTime += " 23:59:59";
            queryWrapper.le("CreateTime", endTime);
        }

        queryWrapper.orderByDesc("CreateTime");

        IPage<HistroydataEntity> page = this.page(new Query<HistroydataEntity>().getPage(params),
                queryWrapper);
        PageUtils pageUtils = new PageUtils(page);
        List<HistroydataEntity> entities = (List<HistroydataEntity>) pageUtils.getList();
        MachineEntity finalMachineEntity = machineEntity;


        //封装数据
        List<Map<String, String>> arrayList = new ArrayList<>();

        List<HistoryDataVo> list = entities.stream().map(entity -> {
            HistoryDataVo historyDataVo = new HistoryDataVo();
            DatatemplateEntity datatemplateEntity = datatemMap.get(entity.getDatatemplateid());
            historyDataVo.setControllerName(datatemplateEntity.getControlname());
            historyDataVo.setUnit(datatemplateEntity.getUnitname());
            historyDataVo.setCustomerName(customerEntity.getCustomername());
            historyDataVo.setDataValue(entity.getDatavalue());
            historyDataVo.setTime(entity.getCreatetime());
            return historyDataVo;
        }).collect(Collectors.toList());

        int i = 0;
        if (entities.size() < limit) {
            limit = entities.size();
        }
        while (i < limit) {
            HashMap<String, String> map = new HashMap<>();
            map.put("MachineName", machineName);
            map.put("CustomerName", customerEntity.getCustomername());
            map.put("time", list.get(i).getTime());
            int total = i + size;
            for (; i < total; i++) {
                switch (list.get(i).getUnit()) {
                    case "开/关":
                        if (list.get(i).getDataValue().equals("null")) {
                            map.put(list.get(i).getControllerName(), "null");
                        } else if (list.get(i).getDataValue().contains("1")) {
                            map.put(list.get(i).getControllerName(), "开");
                        } else {
                            map.put(list.get(i).getControllerName(), "关");
                        }
                        break;
                    default:
                        map.put(list.get(i).getControllerName(), list.get(i).getDataValue() + list.get(i).getUnit());
                }
            }
            arrayList.add(map);
        }

        RealDataVo realDataVo = new RealDataVo();
        realDataVo.setCurrPage(Integer.valueOf(currentpage));
        realDataVo.setTotalCount(pageUtils.getTotalCount() / size);
        realDataVo.setData(arrayList);
        realDataVo.setPageSize(Integer.valueOf(pagesize));
        return realDataVo;
    }

    @Override
    @Async
    public void saveHistorydata(HistroydataEntity histroydata) {
        this.baseMapper.saveHistorydata(histroydata);
    }

    @Async
    public void saveHistorydataBatch(List<HistroydataEntity> entities) {
        List<HistroydataEntity> histroydataEntities = entities.stream().sorted(
                Comparator.comparing(HistroydataEntity::getDatatemplateid)
        ).collect(Collectors.toList());

        for (int i = 0; i < histroydataEntities.size(); i++) {
            this.baseMapper.saveHistorydata(histroydataEntities.get(i));
        }
    }

    @Override
    public List<CurveVo> curve(Map<String, Object> params) throws InterruptedException, ExecutionException {
        String machineName = (String) params.get("machineName");
        String datatemplateId = (String) params.get("datatemplateId");
        String startTime = (String) params.get("startTime");
        String endTime = (String) params.get("endTime");

        if (StringUtils.isEmpty(machineName) || StringUtils.isEmpty(datatemplateId) || StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {
            return null;
        }

        MachineEntity machineEntity = null;

        Long machineId = null;

        if (!StringUtils.isEmpty(machineName)) {
            machineEntity = machineService.getOne(new QueryWrapper<MachineEntity>().eq("MachineName", machineName));
            if (machineEntity != null) {
                machineId = machineEntity.getId();
            }
        }

        //1、查询一天的情况
        if (endTime.equals(startTime)) {
            endTime += " 23:59:59";
            List<CurveVo> list = this.baseMapper.listOne(machineId, datatemplateId, startTime, endTime);
            int size = list.size();
            List<CurveVo> res = new ArrayList<>();
            int count = 0;
            for (int i = 0; i < 24; i++) {
                String hour = String.valueOf(i);
                if (count < size && list.get(count).getName().equals(hour))
                    res.add(list.get(count++));
                else {
                    CurveVo curveVo = new CurveVo(hour, "null");
                    res.add(curveVo);
                }
            }
            return res;
        } else {
            //将时间段拆分为几小段
            Date startDate = convertString2Date("yyyy/MM/dd", startTime);
            Date endDate = convertString2Date("yyyy/MM/dd", endTime);
            List<String> list = new ArrayList<>();
            while (startDate.getTime() <= endDate.getTime()) {
                list.add(convertDate2String("yyyy/MM/dd", startDate));
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(startDate);
                calendar.add(Calendar.DATE, 5);
                if (calendar.getTime().getTime() > endDate.getTime()) {
                    if (!startDate.equals(endDate)) {
                        list.add(convertDate2String("yyyy/MM/dd", endDate));
                    }
                    startDate = calendar.getTime();
                } else {
                    startDate = calendar.getTime();
                }
            }//此时list中转载的就是分段后的数据集合

            //创建线程池
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            List<CurveVo> collect = new ArrayList<>();//返回结果
            List<Callable<List<CurveVo>>> tasks = new ArrayList<>();

            for (int i = 0; i < list.size() - 1; i++) {
                Callable<List<CurveVo>> callable = getCurveVos(machineId, datatemplateId, list.get(i), list.get(i + 1));
                tasks.add(callable);
            }
            //Future用于获取结果
            List<Future<List<CurveVo>>> futures = executorService.invokeAll(tasks);
            //处理线程返回结果
            if (futures != null && futures.size() > 0) {
                for (Future<List<CurveVo>> future : futures) {
                    collect.addAll(future.get());
                }
            }
            List<CurveVo> result = collect.stream().distinct().collect(Collectors.toList());
            executorService.shutdown();
            return result;
        }
    }

    private Callable<List<CurveVo>> getCurveVos(Long machineId, String datatemplateId, String startTime, String endTime) {
        endTime += " 23:59:59";
        String finalEndTime = endTime;
        Callable<List<CurveVo>> callable = new Callable<List<CurveVo>>() {
            @Override
            public List<CurveVo> call() throws Exception {
                //System.out.println(Thread.currentThread().getName() + "开始时间" + startTime + "结束时间" + finalEndTime);
                List<CurveVo> curveVos = histroydataDao.listDate(machineId, datatemplateId, startTime, finalEndTime);
                Date startDate = convertString2Date("yyyy/MM/dd", startTime);
                Date endDate = convertString2Date("yyyy/MM/dd", finalEndTime);
                List<String> list = new ArrayList<>();
                while (startDate.getTime() <= endDate.getTime()) {
                    list.add(convertDate2String("yyyy/MM/dd", startDate));
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(startDate);
                    calendar.add(Calendar.DATE, 1);
                    if (calendar.getTime().getTime() > endDate.getTime()) {
                        if (!startDate.equals(endDate)) {
                            list.add(convertDate2String("yyyy-MM-dd", endDate));
                        }
                        startDate = calendar.getTime();
                    } else {
                        startDate = calendar.getTime();
                    }
                }
                int size = curveVos.size(), count = 0;
                List<CurveVo> res = new ArrayList<>();
                for (int i = 0; i < list.size(); i++) {
                    String day = list.get(i);
                    if (count < size && curveVos.get(count).getName().equals(day))
                        res.add(curveVos.get(count++));
                    else {
                        CurveVo curveVo = new CurveVo(day, "null");
                        res.add(curveVo);
                    }
                }
                return res;
            }
        };
        return callable;
    }

    /**
     * 设置sheet
     */
    private void setSheet(Sheet sheet) {
        // 设置各列宽度(单位为:字符宽度的1/256)
        sheet.setColumnWidth(0, 32 * 256);
        sheet.setColumnWidth(1, 32 * 256);
        sheet.setColumnWidth(2, 20 * 256);
        sheet.setColumnWidth(3, 20 * 256);
        sheet.setColumnWidth(4, 20 * 256);
        sheet.setColumnWidth(5, 20 * 256);
        sheet.setColumnWidth(6, 20 * 256);
        sheet.setColumnWidth(7, 20 * 256);
        sheet.setColumnWidth(8, 20 * 256);
        sheet.setColumnWidth(9, 20 * 256);
        sheet.setColumnWidth(10, 32 * 256);
    }

    /**
     * 获取并设置header样式
     */
    private XSSFCellStyle getAndSetXSSFCellStyleHeader(SXSSFWorkbook sxssfWorkbook) {
        XSSFCellStyle xssfCellStyle = (XSSFCellStyle) sxssfWorkbook.createCellStyle();
        Font font = sxssfWorkbook.createFont();
        // 字体大小
        font.setFontHeightInPoints((short) 14);
        // 字体粗细
        //font.setsetBoldweight((short) 20);
        // 将字体应用到样式上面
        xssfCellStyle.setFont(font);
        // 是否自动换行
        xssfCellStyle.setWrapText(false);
        // 水平居中
        xssfCellStyle.setAlignment(HorizontalAlignment.CENTER);
        // 垂直居中
        xssfCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        return xssfCellStyle;
    }

    /**
     * 获取并设置样式一
     */
    private XSSFCellStyle getAndSetXSSFCellStyleOne(SXSSFWorkbook sxssfWorkbook) {
        XSSFCellStyle xssfCellStyle = (XSSFCellStyle) sxssfWorkbook.createCellStyle();
        XSSFDataFormat format = (XSSFDataFormat) sxssfWorkbook.createDataFormat();
        // 是否自动换行
        xssfCellStyle.setWrapText(false);
        // 水平居中
        xssfCellStyle.setAlignment(HorizontalAlignment.CENTER);
        // 垂直居中
        xssfCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 前景颜色
        xssfCellStyle.setFillForegroundColor(IndexedColors.AQUA.getIndex());
        // 边框
        xssfCellStyle.setBorderBottom(BorderStyle.THIN);
        xssfCellStyle.setBorderRight(BorderStyle.THIN);
        xssfCellStyle.setBorderTop(BorderStyle.THIN);
        xssfCellStyle.setBorderLeft(BorderStyle.THIN);
        xssfCellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        xssfCellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
        xssfCellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
        xssfCellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
        // 防止数字过长,excel导出后,显示为科学计数法,如:防止8615192053888被显示为8.61519E+12
        xssfCellStyle.setDataFormat(format.getFormat("0"));
        return xssfCellStyle;
    }

    /**
     * 获取并设置样式二
     */
    private XSSFCellStyle getAndSetXSSFCellStyleTwo(SXSSFWorkbook sxssfWorkbook) {
        XSSFCellStyle xssfCellStyle = (XSSFCellStyle) sxssfWorkbook.createCellStyle();
        XSSFDataFormat format = (XSSFDataFormat) sxssfWorkbook.createDataFormat();
        // 是否自动换行
        xssfCellStyle.setWrapText(false);
        // 水平居中
        xssfCellStyle.setAlignment(HorizontalAlignment.CENTER);
        // 边框
        xssfCellStyle.setBorderBottom(BorderStyle.THIN);
        xssfCellStyle.setBorderRight(BorderStyle.THIN);
        xssfCellStyle.setBorderTop(BorderStyle.THIN);
        xssfCellStyle.setBorderLeft(BorderStyle.THIN);
        xssfCellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
        xssfCellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
        xssfCellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
        xssfCellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
        // 垂直居中
        xssfCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 防止数字过长,excel导出后,显示为科学计数法,如:防止8615192053888被显示为8.61519E+12
        xssfCellStyle.setDataFormat(format.getFormat("0"));
        return xssfCellStyle;
    }


    public static String convertDate2String(String format, Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        return simpleDateFormat.format(date);
    }


    public static Date convertString2Date(String format, String dateStr) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        try {
            Date date = simpleDateFormat.parse(dateStr.toString());
            return date;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}